package com.ruoyi.pack.service.impl;

import com.ruoyi.pack.service.ILocationProcessingService;
import com.ruoyi.pack.service.IPackLocatorService;
import com.ruoyi.pack.service.IPackLocationLogService;
import com.ruoyi.pack.service.IWayBillService;
import com.ruoyi.pack.service.ITransportPlanService;
import com.ruoyi.pack.domain.WayBill;
import com.ruoyi.pack.domain.TransportPlan;
import com.ruoyi.pack.dto.LocationUploadRequest;
import com.ruoyi.pack.dto.LocationInfo;
import com.ruoyi.pack.domain.PackLocator;
import com.ruoyi.pack.domain.PackLocationLog;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.utils.AmapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 位置处理服务实现类
 *
 * @author ruoyi
 * @date 2024-01-01
 */
@Service
public class LocationProcessingServiceImpl implements ILocationProcessingService {

    private static final Logger log = LoggerFactory.getLogger(LocationProcessingServiceImpl.class);

    @Autowired
    private IPackLocatorService packLocatorService;

    @Autowired
    private IPackLocationLogService packLocationLogService;

    @Autowired
    private IWayBillService wayBillService;

    @Autowired
    private AmapUtils amapUtils;

    @Autowired
    private ITransportPlanService transportPlanService;

    /**
     * 处理定位器位置信息上传
     */
    @Override
    @Transactional
    public boolean processLocationUpload(LocationUploadRequest request) {
        try {
            log.info("开始处理位置信息上传, 定位器编码: {}, 经纬度: {},{}",
                request.getLocatorCode(), request.getLongitude(), request.getLatitude());

            // 1. 验证必填参数的完整性
            if (!validateRequiredParams(request)) {
                log.warn("必填参数不完整: {}", request.getLocatorCode());
                return false;
            }

            // 2. 验证定位器编码在数据库中是否存在
            PackLocator locator = packLocatorService.selectPackLocatorByLocatorCode(request.getLocatorCode());
            if (locator == null) {
                log.warn("定位器不存在: {}", request.getLocatorCode());
                return false;
            }

            // 3. 验证GPS坐标是否在合理范围内（中国境内）
            if (!isValidCoordinates(request.getLongitude(), request.getLatitude())) {
                log.warn("GPS坐标不在中国境内: {},{}", request.getLongitude(), request.getLatitude());
                return false;
            }

            // 4. 根据经纬度调用高德地址接口查询地址信息
            java.util.Map<String, String> addressInfo = getAddressInfoFromCoordinates(request.getLongitude(), request.getLatitude());
            String address = addressInfo.get("address");

            // 5. 通过定位器编码查询绑定的运单ID
            Long waybillId = getWaybillIdByLocatorCode(request.getLocatorCode());

            // 6. 更新运单表的位置信息
            if (waybillId != null) {
                updateWayBillLocationInfo(waybillId, request, addressInfo);
            }

            // 7. 保存位置历史轨迹记录
            saveLocationLog(locator, request, address, waybillId, addressInfo);

            // 8. 城市变化检测和运输计划更新（放在保存位置日志之后）
            if (waybillId != null) {
                checkAndUpdateCityArrival(waybillId, locator.getLocatorId(), addressInfo);
            }

            // 9. 更新定位器状态
            updateLocatorStatus(locator, request, waybillId);

            log.info("位置信息处理完成, 定位器编码: {}, 地址: {}, 运单ID: {}",
                locator.getLocatorCode(), address, waybillId);
            return true;

        } catch (Exception e) {
            log.error("处理位置信息上传失败", e);
            return false;
        }
    }

    /**
     * 验证必填参数的完整性
     */
    private boolean validateRequiredParams(LocationUploadRequest request) {
        // 验证必填参数：locatorCode, latitude, longitude
        if (request.getLocatorCode() == null || request.getLocatorCode().trim().isEmpty()) {
            log.warn("定位器编码为空");
            return false;
        }

        if (request.getLatitude() == null) {
            log.warn("纬度参数为空");
            return false;
        }

        if (request.getLongitude() == null) {
            log.warn("经度参数为空");
            return false;
        }

        return true;
    }

    /**
     * 验证GPS坐标是否在合理范围内（中国境内）
     */
    private boolean isValidCoordinates(BigDecimal longitude, BigDecimal latitude) {
        if (longitude == null || latitude == null) {
            return false;
        }

        // 中国境内经纬度范围验证
        double lng = longitude.doubleValue();
        double lat = latitude.doubleValue();

        // 中国经度范围：73°E-135°E，纬度范围：18°N-54°N
        return lng >= 73.0 && lng <= 135.0 && lat >= 18.0 && lat <= 54.0;
    }

    /**
     * 通过定位器编码查询绑定的运单ID
     */
    private Long getWaybillIdByLocatorCode(String locatorCode) {
        try {
            // 通过定位器编码查询绑定的运单
            WayBill wayBill = wayBillService.selectWayBillByLocatorCode(locatorCode);
            if (wayBill != null) {
                log.debug("定位器 {} 绑定的运单ID: {}", locatorCode, wayBill.getId());
                return Long.parseLong(wayBill.getId());
            } else {
                log.debug("定位器 {} 未绑定运单", locatorCode);
                return null;
            }
        } catch (Exception e) {
            log.error("查询定位器绑定的运单失败: {}", locatorCode, e);
            return null;
        }
    }

    /**
     * 根据经纬度调用高德地址接口查询地址信息
     */
    private java.util.Map<String, String> getAddressInfoFromCoordinates(BigDecimal longitude, BigDecimal latitude) {
        try {
            log.info("开始调用高德地图逆地理编码API，经纬度: {},{}", longitude, latitude);

            // 使用AmapUtils工具类进行地址解析
            java.util.Map<String, String> addressInfo = amapUtils.getAddressFromCoordinate(longitude, latitude);

            if (addressInfo != null && !addressInfo.isEmpty()) {
                // 如果没有格式化地址，尝试组合省市区信息作为默认地址
                String formattedAddress = addressInfo.get("address");
                if (formattedAddress == null || formattedAddress.trim().isEmpty()) {
                    StringBuilder addressBuilder = new StringBuilder();
                    String province = addressInfo.get("province");
                    String city = addressInfo.get("city");
                    String district = addressInfo.get("district");

                    if (province != null && !province.trim().isEmpty()) {
                        addressBuilder.append(province);
                    }
                    if (city != null && !city.trim().isEmpty()) {
                        addressBuilder.append(city);
                    }
                    if (district != null && !district.trim().isEmpty()) {
                        addressBuilder.append(district);
                    }

                    String combinedAddress = addressBuilder.toString();
                    if (!combinedAddress.isEmpty()) {
                        addressInfo.put("address", combinedAddress);
                        log.debug("组合地址解析成功: {}", combinedAddress);
                    } else {
                        addressInfo.put("address", "地址解析失败");
                    }
                } else {
                    log.debug("地址解析成功: {}", formattedAddress);
                }

                return addressInfo;
            }

            log.warn("地址解析失败，未获取到有效地址信息");
            // 返回包含默认值的Map
            java.util.Map<String, String> defaultInfo = new java.util.HashMap<>();
            defaultInfo.put("address", "地址解析失败");
            return defaultInfo;

        } catch (Exception e) {
            log.error("调用高德地图API失败", e);
            // 返回包含异常信息的Map
            java.util.Map<String, String> errorInfo = new java.util.HashMap<>();
            errorInfo.put("address", "地址解析异常");
            return errorInfo;
        }
    }

    /**
     * 更新运单表的位置信息
     */
    private void updateWayBillLocationInfo(Long waybillId, LocationUploadRequest request, java.util.Map<String, String> addressInfo) {
        try {
            log.info("开始更新运单位置信息, 运单ID: {}", waybillId);

            // 查询运单信息（验证运单是否存在）
            WayBill wayBill = wayBillService.getById(waybillId.toString());
            if (wayBill == null) {
                log.warn("运单不存在，无法更新位置信息: {}", waybillId);
                return;
            }

            // 基于查询到的完整运单对象进行更新操作

            // 更新经纬度
            wayBill.setCurrentLongitude(request.getLongitude());
            wayBill.setCurrentLatitude(request.getLatitude());

            // 更新地址信息
            if (addressInfo != null && !addressInfo.isEmpty()) {
                String province = addressInfo.get("province");
                String city = addressInfo.get("city");
                String district = addressInfo.get("district");
                String address = addressInfo.get("address");

                wayBill.setCurrentProvince(province);
                wayBill.setCurrentCity(city);
                wayBill.setCurrentDistrict(district);
                wayBill.setCurrentAddress(address);
                wayBill.setArrivalTime(new Date());
                wayBill.setLocationUpdateTime(new Date());
                
                if(StringUtils.equals(city,wayBill.getReceiverCity())) {
                    wayBill.setBillStatus("已送达");
                    
                }
                log.debug("更新运单地址信息: 省={}, 市={}, 区={}, 详细地址={}",
                    province, city, district, address);
            }

            // 设置位置更新时间为当前时间
            wayBill.setLocationUpdateTime(new Date());
            // TODO 查询运输计划表，获取预计到达时间，如果当前时间大于预计到达时间，则更新运单状态为“运输中”，否则为“延迟”

            // 调用服务层更新方法
            int result = wayBillService.updateWayBillLocation(wayBill);
            if (result > 0) {
                log.info("运单位置信息更新成功, 运单ID: {}", waybillId);
            } else {
                log.warn("运单位置信息更新失败, 运单ID: {}", waybillId);
            }

        } catch (Exception e) {
            log.error("更新运单位置信息异常, 运单ID: {}", waybillId, e);
        }
    }

    /**
     * 保存位置历史轨迹记录
     */
    private void saveLocationLog(PackLocator locator, LocationUploadRequest request, String address, Long waybillId, Map<String, String> addressInfo) {
        PackLocationLog locationLog = new PackLocationLog();
        locationLog.setLocatorId(locator.getLocatorId());
        locationLog.setLocatorCode(locator.getLocatorCode());

        // 优先使用查询到的运单ID，如果没有则使用定位器中的containerId
        if (waybillId != null) {
            locationLog.setContainerId(waybillId);
            log.debug("使用查询到的运单ID: {}", waybillId);
        } else {
            locationLog.setContainerId(locator.getContainerId());
            log.debug("使用定位器中的containerId: {}", locator.getContainerId());
        }

        locationLog.setContainerCode(locator.getContainerCode());
        locationLog.setUploadTime(request.getUploadTime() != null ? request.getUploadTime() : new Date());
        locationLog.setLatitude(request.getLatitude());
        locationLog.setLongitude(request.getLongitude());
        locationLog.setUploadAddress(address); // 设置地址信息

        // 🔧 新增：设置地址解析字段
        if (addressInfo != null) {
            locationLog.setProvince(addressInfo.get("province"));
            locationLog.setCity(addressInfo.get("city"));
            locationLog.setDistrict(addressInfo.get("district"));
            log.debug("设置地址解析字段: 省份={}, 城市={}, 区县={}",
                locationLog.getProvince(), locationLog.getCity(), locationLog.getDistrict());
        }

        // 设置其他传感器数据
        if (request.getBatteryLevel() != null) {
            locationLog.setBatteryLevel(request.getBatteryLevel().intValue());
        }

        packLocationLogService.insertPackLocationLog(locationLog);
        log.debug("保存位置日志记录完成, 地址: {}, 城市: {}, 运单ID: {}",
            address, locationLog.getCity(), locationLog.getContainerId());
    }

    /**
     * 更新定位器状态
     */
    private void updateLocatorStatus(PackLocator locator, LocationUploadRequest request, Long waybillId) {
        try {
            locator.setLastOnlineTime(request.getUploadTime() != null ? request.getUploadTime() : new Date());
            if (request.getBatteryLevel() != null) {
                locator.setBatteryLevel(request.getBatteryLevel());
            }
            if (request.getSignalStrength() != null) {
                locator.setSignalStrength(request.getSignalStrength());
            }
            locator.setLocatorStatus("1"); // 正常状态

            // 如果查询到运单ID，则更新定位器的containerId字段
            if (waybillId != null) {
                locator.setContainerId(waybillId);
                log.debug("更新定位器containerId为运单ID: {}", waybillId);
            }

            packLocatorService.updatePackLocator(locator);
            log.debug("定位器状态更新完成, 运单ID: {}", waybillId);
        } catch (Exception e) {
            log.error("更新定位器状态失败", e);
        }
    }

    @Override
    public LocationInfo getLocationInfo(BigDecimal longitude, BigDecimal latitude) {
        // TODO: 实现地理编码服务调用（高德地图API）
        // 这里返回模拟数据，实际应该调用地理编码服务
        LocationInfo locationInfo = new LocationInfo();
        locationInfo.setProvince("广东省");
        locationInfo.setCity("深圳市");
        locationInfo.setDistrict("南山区");
        locationInfo.setFormattedAddress("广东省深圳市南山区");
        return locationInfo;
    }

    @Override
    public double calculateDistance(BigDecimal lng1, BigDecimal lat1, BigDecimal lng2, BigDecimal lat2) {
        if (lng1 == null || lat1 == null || lng2 == null || lat2 == null) {
            return Double.MAX_VALUE;
        }

        double earthRadius = 6371; // 地球半径，单位：公里

        double lat1Rad = Math.toRadians(lat1.doubleValue());
        double lat2Rad = Math.toRadians(lat2.doubleValue());
        double deltaLat = Math.toRadians(lat2.doubleValue() - lat1.doubleValue());
        double deltaLng = Math.toRadians(lng2.doubleValue() - lng1.doubleValue());

        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                   Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return earthRadius * c;
    }

    @Override
    public Double getAverageTransportTime(String fromCity, String toCity) {
        // TODO: 实现历史运输时间查询
        // 这里返回模拟数据，实际应该查询历史运单数据
        return 24.0; // 模拟24小时平均运输时间
    }

    @Override
    public void sendDelayAlert(String wayBillId, int delayHours) {
        // TODO: 实现延迟预警发送（邮件、短信、系统通知等）
        log.info("发送延迟预警: 运单ID={}, 延迟小时数={}", wayBillId, delayHours);
    }

    @Override
    public void sendArrivalNotification(String wayBillId) {
        // TODO: 实现到达通知发送（邮件、短信、系统通知等）
        log.info("发送到达通知: 运单ID={}", wayBillId);
    }

    /**
     * 检测城市变化并更新运输计划到达时间
     *
     * @param waybillId 运单ID
     * @param locatorId 定位器ID
     * @param currentAddressInfo 当前地址信息
     */
    private void checkAndUpdateCityArrival(Long waybillId, Long locatorId, Map<String, String> currentAddressInfo) {
        try {
            log.info("开始检测城市变化，运单ID: {}", waybillId);

            // 1. 查询当前订单倒数第二条定位日志的城市（排除刚保存的当前记录）
            PackLocationLog previousLog = packLocationLogService.selectSecondLatestLogByWaybillId(waybillId, locatorId);
            String previousCity = null;

            if (previousLog != null) {
                previousCity = previousLog.getCity();
                log.info("从定位日志获取上次城市: {}", previousCity);
            } else {
                // 如果没有历史定位记录，使用运输计划表中起点的城市作为上一次城市
                TransportPlan startPoint = transportPlanService.selectStartPointByWaybillId(waybillId);
                if (startPoint != null) {
                    previousCity = startPoint.getCity();
                    log.info("未找到历史定位记录，使用运输计划起点城市作为上次城市: {}", previousCity);
                } else {
                    log.info("未找到历史定位记录和运输计划起点，跳过城市变化检测");
                    return;
                }
            }
            String currentCity = currentAddressInfo.get("city");

            log.info("城市对比详情 - 运单ID: {}, 上次定位城市: '{}', 当前定位城市: '{}'",
                waybillId, previousCity, currentCity);

            // 调试：输出详细的地址信息
            log.info("当前地址信息详情: {}", currentAddressInfo);

            // 2. 对比城市是否变化
            if (previousCity != null && currentCity != null && !previousCity.equals(currentCity)) {
                log.info("✅ 检测到城市变化: '{}' -> '{}', 开始更新运输计划", previousCity, currentCity);

                // 3. 查询当前城市对应的运输计划
                TransportPlan currentCityPlan = transportPlanService.selectTransportPlanByWaybillIdAndCity(waybillId, currentCity);
                if (currentCityPlan != null) {
                    log.info("找到对应的运输计划: ID={}, 城市={}, 节点类型={}, 顺序={}",
                        currentCityPlan.getId(), currentCityPlan.getCity(),
                        currentCityPlan.getNodeType(), currentCityPlan.getPassOrder());

                    Date currentTime = new Date();

                    // 4. 计算实际耗时
                    Double actualDuration = calculateActualDuration(waybillId, currentTime);

                    // 5. 计算偏差信息
                    Double arrivalDeviation = null;
                    String deviationStatus = null;
                    
                    if (actualDuration != null && currentCityPlan.getEstimatedDuration() != null) {
                        Double estimatedDuration = currentCityPlan.getEstimatedDuration();
                        double maxDev = currentCityPlan.getMaxDeviation() != null ? currentCityPlan.getMaxDeviation() : 0.0;
                        
                        // 判断偏差状态：以预计耗时为中心的±最大偏差区间（与延迟状态判断逻辑一致）
                        if (actualDuration > estimatedDuration + maxDev) {
                            deviationStatus = "延迟";
                            arrivalDeviation = actualDuration - (estimatedDuration + maxDev);
                        } else if (actualDuration < estimatedDuration - maxDev) {
                            deviationStatus = "提前";
                            arrivalDeviation = actualDuration - (estimatedDuration - maxDev);
                        } else {
                            deviationStatus = "正点";
                            arrivalDeviation = actualDuration - estimatedDuration;
                        }
                        
                        // 保留一位小数
                        arrivalDeviation = Math.round(arrivalDeviation * 10.0) / 10.0;
                    }

                    // 6. 更新运输计划的到达信息（包含偏差数据）
                    int updateResult = transportPlanService.updateArrivalInfoWithDeviation(
                        currentCityPlan.getId(),
                        currentTime,
                        actualDuration,
                        arrivalDeviation,
                        deviationStatus
                    );

                    if (updateResult > 0) {
                        log.info("✅ 成功更新运输计划，运输计划ID: {}, 城市: {}, 到达时间: {}, 实际耗时: {}小时, 偏差: {}小时, 状态: {}",
                            currentCityPlan.getId(), currentCity, currentTime, actualDuration, arrivalDeviation, deviationStatus);

                        // 新增：基于最大耗时偏差判断运单状态
                        updateWayBillStatusBasedOnDeviation(waybillId, currentCityPlan, actualDuration);

                        // 4. 判断当前城市是否为终点城市，如果是则更新运单状态为已送达
                        if ("终点".equals(currentCityPlan.getNodeType())) {
                            log.info("🎯 检测到到达终点城市: {}, 开始更新运单状态为已送达", currentCity);
                            // 先更新延迟状态，再更新运单状态为已送达
                            updateEndPointDelayStatus(waybillId, currentCityPlan, actualDuration);
                            updateWayBillStatusToDelivered(waybillId);
                        }
                    } else {
                        log.error("❌ 更新运输计划失败，运输计划ID: {}", currentCityPlan.getId());
                    }
                } else {
                    log.warn("❌ 未找到城市 '{}' 对应的运输计划，运单ID: {}", currentCity, waybillId);
                    // 调试：查询该运单的所有运输计划
                    TransportPlan query = new TransportPlan();
                    query.setWaybillId(waybillId);
                    List<TransportPlan> allPlans = transportPlanService.selectTransportPlanList(query);
                    log.info("该运单的所有运输计划：");
                    for (TransportPlan plan : allPlans) {
                        log.info("  - ID: {}, 城市: '{}', 节点类型: {}, 顺序: {}",
                            plan.getId(), plan.getCity(), plan.getNodeType(), plan.getPassOrder());
                    }
                }
            } else {
                String reason = "";
                if (previousCity == null) reason = "上次城市为null";
                else if (currentCity == null) reason = "当前城市为null";
                else if (previousCity.equals(currentCity)) reason = "城市相同";

                log.info("⚪ 城市未发生变化，无需更新运输计划。原因: {}", reason);
            }

        } catch (Exception e) {
            log.error("检测城市变化并更新运输计划失败，运单ID: {}", waybillId, e);
            // 这里不抛出异常，避免影响主流程
        }
    }

    /**
     * 计算从起点到当前时间的实际耗时
     *
     * @param waybillId 运单ID
     * @param currentTime 当前时间
     * @return 实际耗时（小时）
     */
    private Double calculateActualDuration(Long waybillId, Date currentTime) {
        try {
            // 查询起点运输计划
            TransportPlan startPoint = transportPlanService.selectStartPointByWaybillId(waybillId);
            if (startPoint != null && startPoint.getArrivalTime() != null) {
                // 计算时间差（毫秒）
                long timeDiffMillis = currentTime.getTime() - startPoint.getArrivalTime().getTime();
                // 转换为小时
                double hours = timeDiffMillis / (1000.0 * 60 * 60);
                return Math.round(hours * 100.0) / 100.0; // 保留两位小数
            } else {
                log.warn("未找到起点运输计划或起点到达时间为空，运单ID: {}", waybillId);
                return null;
            }
        } catch (Exception e) {
            log.error("计算实际耗时失败，运单ID: {}", waybillId, e);
            return null;
        }
    }

    /**
     * 更新运单状态为已送达
     *
     * @param waybillId 运单ID
     */
    private void updateWayBillStatusToDelivered(Long waybillId) {
        try {
            // 查询运单信息
            WayBill wayBill = wayBillService.getById(waybillId);
            if (wayBill == null) {
                log.error("更新运单状态失败：运单不存在，运单ID: {}", waybillId);
                return;
            }

            // 检查当前状态，避免重复更新
            if ("已送达".equals(wayBill.getBillStatus())) {
                log.info("运单状态已是已送达，无需重复更新，运单ID: {}", waybillId);
                return;
            }

            // 更新运单状态为已送达
            wayBill.setBillStatus("已送达");
            wayBill.setArrivalTime(new Date()); // 设置实际到达时间

            int updateResult = wayBillService.updateWayBillLocation(wayBill);
            if (updateResult > 0) {
                log.info("🎉 成功更新运单状态为已送达，运单ID: {}, 实际到达时间: {}", 
                    waybillId, wayBill.getArrivalTime());
            } else {
                log.error("❌ 更新运单状态为已送达失败，运单ID: {}", waybillId);
            }

        } catch (Exception e) {
            log.error("更新运单状态为已送达异常，运单ID: {}", waybillId, e);
        }
    }

    /**
     * 基于最大耗时偏差判断并更新运单延迟状态
     * 
     * @param waybillId 运单ID
     * @param currentCityPlan 当前城市的运输计划
     * @param actualDuration 实际耗时（小时）
     */
    private void updateWayBillStatusBasedOnDeviation(Long waybillId, TransportPlan currentCityPlan, Double actualDuration) {
        try {
            // 跳过终点的状态判断，终点由专门的方法处理
            if ("终点".equals(currentCityPlan.getNodeType())) {
                log.debug("跳过终点城市的延迟状态判断，运单ID: {}", waybillId);
                return;
            }

            Double estimatedDuration = currentCityPlan.getEstimatedDuration(); // 预计耗时
            Double maxDeviation = currentCityPlan.getMaxDeviation(); // 最大耗时偏差（2σ）

            // 参数有效性检查
            if (actualDuration == null || estimatedDuration == null || maxDeviation == null) {
                log.warn("参数不完整，跳过延迟状态判断 - 运单ID: {}, 实际耗时: {}, 预计耗时: {}, 最大偏差: {}", 
                    waybillId, actualDuration, estimatedDuration, maxDeviation);
                return;
            }

            // 延迟小时数 保留一位小数
            double delayHours = 0.0;
            
            // 判断延迟状态：以预计耗时为中心的±最大偏差区间
            String delayStatus;
            String logMessage;
            
            if (actualDuration > estimatedDuration + maxDeviation) {
                delayStatus = "延迟";
                delayHours = actualDuration - (estimatedDuration + maxDeviation);
                logMessage = String.format("运输延迟，实际耗时%.1f小时超出上限%.1f小时", 
                    actualDuration, estimatedDuration + maxDeviation);
            } else if (actualDuration < estimatedDuration - maxDeviation) {
                delayStatus = "提前";
                delayHours = actualDuration - (estimatedDuration - maxDeviation);
                logMessage = String.format("运输提前，实际耗时%.1f小时低于下限%.1f小时", 
                    actualDuration, estimatedDuration - maxDeviation);
            } else {
                delayStatus = "正点";
                delayHours = actualDuration - estimatedDuration;
                logMessage = String.format("运输正常，实际耗时%.1f小时在预期范围[%.1f, %.1f]内", 
                    actualDuration, estimatedDuration - maxDeviation, estimatedDuration + maxDeviation);
            }
            log.info("🚛 运单延迟状态判断 - 运单ID: {}, 城市: {}, {}", 
                waybillId, currentCityPlan.getCity(), logMessage);

            // 更新运单的延迟状态和延迟小时数
            updateWayBillDelayStatus(waybillId, delayStatus, delayHours);

        } catch (Exception e) {
            log.error("基于偏差更新运单延迟状态失败，运单ID: {}", waybillId, e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 更新终点延迟状态
     * 
     * @param waybillId 运单ID
     * @param endPointPlan 终点运输计划
     * @param actualDuration 实际耗时（小时）
     */
    private void updateEndPointDelayStatus(Long waybillId, TransportPlan endPointPlan, Double actualDuration) {
        try {
            log.info("🎯 开始计算终点延迟状态 - 运单ID: {}, 城市: {}", waybillId, endPointPlan.getCity());

            Double estimatedDuration = endPointPlan.getEstimatedDuration(); // 预计耗时
            Double maxDeviation = endPointPlan.getMaxDeviation(); // 最大耗时偏差（2σ）

            // 参数有效性检查
            if (actualDuration == null || estimatedDuration == null || maxDeviation == null) {
                log.warn("终点延迟状态计算参数不完整，跳过延迟状态判断 - 运单ID: {}, 实际耗时: {}, 预计耗时: {}, 最大偏差: {}", 
                    waybillId, actualDuration, estimatedDuration, maxDeviation);
                return;
            }

            // 延迟小时数 保留一位小数
            double delayHours = 0.0;
            
            // 判断延迟状态：以预计耗时为中心的±最大偏差区间
            String delayStatus;
            String logMessage;
            
            if (actualDuration > estimatedDuration + maxDeviation) {
                delayStatus = "延迟";
                delayHours = actualDuration - (estimatedDuration + maxDeviation);
                logMessage = String.format("终点运输延迟，实际耗时%.1f小时超出上限%.1f小时", 
                    actualDuration, estimatedDuration + maxDeviation);
            } else if (actualDuration < estimatedDuration - maxDeviation) {
                delayStatus = "提前";
                delayHours = actualDuration - (estimatedDuration - maxDeviation);
                logMessage = String.format("终点运输提前，实际耗时%.1f小时低于下限%.1f小时", 
                    actualDuration, estimatedDuration - maxDeviation);
            } else {
                delayStatus = "正点";
                delayHours = actualDuration - estimatedDuration;
                logMessage = String.format("终点运输正常，实际耗时%.1f小时在预期范围[%.1f, %.1f]内", 
                    actualDuration, estimatedDuration - maxDeviation, estimatedDuration + maxDeviation);
            }
            
            log.info("🎯 终点延迟状态判断 - 运单ID: {}, 城市: {}, {}", 
                waybillId, endPointPlan.getCity(), logMessage);

            // 更新运单的延迟状态和延迟小时数
            updateWayBillDelayStatus(waybillId, delayStatus, delayHours);

        } catch (Exception e) {
            log.error("计算终点延迟状态失败，运单ID: {}", waybillId, e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 更新运单延迟状态和延迟小时数
     * 
     * @param waybillId 运单ID
     * @param delayStatus 延迟状态
     * @param delayHours 延迟小时数
     */
    private void updateWayBillDelayStatus(Long waybillId, String delayStatus, Double delayHours) {
        try {
            WayBill wayBill = wayBillService.getById(waybillId);
            if (wayBill == null) {
                log.error("更新运单延迟状态失败：运单不存在，运单ID: {}", waybillId);
                return;
            }

            // 检查状态是否需要更新
            if (delayStatus.equals(wayBill.getDelayStatus()) && 
                delayHours != null && delayHours.equals(wayBill.getDelayHours())) {
                log.debug("运单延迟状态已是{}，延迟小时数已是{}，无需重复更新，运单ID: {}", 
                    delayStatus, delayHours, waybillId);
                return;
            }

            String oldDelayStatus = wayBill.getDelayStatus();
            Double oldDelayHours = wayBill.getDelayHours();
            
            // 更新延迟状态和延迟小时数
            wayBill.setDelayStatus(delayStatus);
            wayBill.setDelayHours(delayHours);
            wayBill.setUpdateTime(new Date());

            int updateResult = wayBillService.updateWayBillLocation(wayBill);
            if (updateResult > 0) {
                log.info("🔄 成功更新运单延迟状态: {} -> {}，延迟小时数: {} -> {}，运单ID: {}", 
                    oldDelayStatus, delayStatus, oldDelayHours, delayHours, waybillId);
            } else {
                log.error("❌ 更新运单延迟状态失败，运单ID: {}", waybillId);
            }

        } catch (Exception e) {
            log.error("更新运单延迟状态异常，运单ID: {}, 延迟状态: {}, 延迟小时数: {}", waybillId, delayStatus, delayHours, e);
        }
    }
}
